﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
using System.Threading.Tasks;
using SSLLib;

namespace ExchangeHog.POP3Client
{
	public class POP3Client: SSLEnabledStreamReader
	{

		private void sendCommand(String command)
		{
			byte[] data = ASCIIEncoding.ASCII.GetBytes(command);
			if (sslt != null)
				data = sslt.Encrypt(data);

			sendRaw(data);
			Console.WriteLine("Sent: {0}", command);
		}

		private String readOK(String terminator)
		{
			StringBuilder sb = new StringBuilder();
			bool checkWhetherStartsWithOK = false;
			char[] endofresponse = new char[10];
			while(true)
			{
				String s = read();
				if (s.Length > 0)
				{
					sb.Append(s);
					if (!checkWhetherStartsWithOK)
					{
						expectOK(sb.ToString());
						checkWhetherStartsWithOK = true;
					}
					int posSB = sb.Length - 1;
					int posTerm = terminator.Length - 1;
					while (posTerm >= 0 && posSB >= 0 && terminator[posTerm] == sb[posSB])
					{
						posSB--;
						posTerm--;
					}
					if (posTerm < 0)
						break;
				}
			}
			String responseData = sb.ToString();
			Console.WriteLine("Received: {0}", responseData);
			return responseData;
		}

		private String expectMultilineOK()
		{
			return readOK("\r\n.\r\n");
		}

		private String expectSinglelineOK()
		{
			return readOK("\r\n");
		}

		private void expectOK(String response)
		{
			if (!response.StartsWith("+OK"))
				throw new Exception("Unexpected response : " + response);
		}

		public void authenticate(String user, SecureString password)
		{
			sendCommand("USER " + user + "\n");
			expectSinglelineOK();
			sendCommand("PASS " + Util.Util.ConvertToUnsecureString(password) + "\n");
			expectSinglelineOK();
		}

		public int numberOfMessages()
		{
			sendCommand("STAT\n");

			String response = expectSinglelineOK();
			return Int32.Parse(response.Split(' ')[1]);
		}

		public struct MessageNumberAndUIDL
		{
			public int messagenumber;
			public String uidl;

			public MessageNumberAndUIDL(int messagenumber, String uidl)
			{
				this.messagenumber = messagenumber;
				this.uidl = uidl;
			}
		}

		public LinkedList<MessageNumberAndUIDL> retreiveUIDLs()
		{
			sendCommand("UIDL\n");
			String response = expectMultilineOK();
			String[] num_and_uidls = response.Split('\n');
			var result = new LinkedList<MessageNumberAndUIDL>();
			for (int i = 1; i < num_and_uidls.Length - 2; i++) // skip first and last lines
			{
				String[] nau_split = num_and_uidls[i].Trim().Split(' ');

				result.AddLast(new MessageNumberAndUIDL(Int32.Parse(nau_split[0]), nau_split[1]));
			}
			return result;
		}

		public String retrieve(int num)
		{
			sendCommand("RETR " + num + "\n");
			String response = expectMultilineOK();
			String firstLine = response.Substring(0, response.IndexOf('\n'));

			return response.Substring(firstLine.Length + 1);
		}
	}
}
